﻿using Microsoft.AspNetCore.Mvc.Formatters;
using Microsoft.Extensions.Logging;
using Microsoft.Net.Http.Headers;
using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json;
using RedLockNet.SERedis;
using RedLockNet.SERedis.Configuration;
using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Runtime.Serialization;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Xml;
using Tsual.Util;
using Tsual.Util.Redis;
using Tsual.Wechat.Cache;
using Tsual.Wechat.Message;
using Tsual.Wechat.Message.Exceptions;
using Tsual.Wechat.Message.Impl;
using Tsual.Wechat.Message.Impl.Internal;
using Tsual.Wechat.Message.Impl.Response;
using Tsual.Wechat.Message.Interface.Request;
using Tsual.Wechat.Message.Interface.Response;
using System.Web;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Configuration;
using Microsoft.AspNetCore.Mvc;
using System.Xml.Linq;
using System.Net;
using System.Collections;
using Tsual.Wechat;
using Tsual.Wechat.Formatters;
using Microsoft.AspNetCore.Hosting;
using Tsual.Wechat.Enums;
using Microsoft.Extensions.Hosting;
using Microsoft.AspNetCore.Mvc.ApplicationParts;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.ApplicationModels;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Tsual.Wechat.Filters;
using System.Reflection;
using Tsual.Wechat.Controllers;
using Microsoft.AspNetCore.Mvc.Routing;
using Microsoft.Extensions.Options;
using System.ComponentModel.DataAnnotations;
using Tsual.Wechat.Pipes;

namespace Tsual.Util
{
    public static class NetExtensions
    {
        public static async Task<HttpResponseMessage> GetAsync(this HttpClient client, string BaseRequestUri, params KeyValuePair<string, string>[] args)
        {
            if (args != null && args.Length > 0)
            {
                BaseRequestUri = UrlAppendParam(BaseRequestUri, args);
            }
            return await client.GetAsync(BaseRequestUri);
        }

        public static string UrlAppendParam(string BaseRequestUri, params KeyValuePair<string, string>[] args) => string.Format("{0}?{1}", BaseRequestUri, string.Join("&", args.Select(x => string.Format("{0}={1}", HttpUtility.UrlEncode(x.Key), HttpUtility.UrlEncode(x.Value)))));

        public static MemoryStream OpenAsMemoryStream(this string str, Encoding encoding = null) => new MemoryStream((encoding == null ? Encoding.UTF8 : encoding).GetBytes(str));

    }

    public static class TimeExtensions
    {
        public static TimeSpan GetRemainDuration(this DateTime startTime, int seconds) => TimeSpan.FromSeconds(seconds - (DateTime.Now - startTime).TotalSeconds);

        public static TimeSpan GetRemainDuration(this DateTime startTime, TimeSpan duration) => duration - (DateTime.Now - startTime);
    }
}

namespace Tsual.Util.Redis
{
    public static class RedisExpirableHashExtension
    {
        public static bool HashSet(this IDatabase cache, RedisKey key, RedisValue hashField, RedisValue value, TimeSpan Expire, When when = When.Always, CommandFlags flags = CommandFlags.None)
        {
            var exp = DateTimeOffset.UtcNow.Add(Expire).ToUnixTimeSeconds();
            RedisKey exp_key = "EXPIRE:" + (string)key;
            cache.HashSet(exp_key, hashField, exp, when, flags);
            return cache.HashSet(key, hashField, value, when, flags);
        }

        public static RedisValue HashGetExp(this IDatabase cache, RedisKey key, RedisValue hashField, CommandFlags flags = CommandFlags.None)
        {
            var now = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
            RedisKey exp_key = "EXPIRE:" + (string)key;
            var exp = (long?)cache.HashGet(exp_key, hashField, flags);
            if (exp.HasValue && exp < now)
            {
                cache.HashDelete(key, hashField, flags);
                cache.HashDelete(exp_key, hashField, flags);
            }
            return cache.HashGet(key, hashField, flags);
        }

        public static HashEntry[] ToHashEntryArray(this IEnumerable<KeyValuePair<string, string>> enumable) => enumable.Select(t => new HashEntry(t.Key, t.Value)).ToArray();
    }
}